package org.gpslw.util;


import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.feature.simple.SimpleFeatureType;
import org.geotools.api.filter.identity.FeatureId;
import org.geotools.api.referencing.FactoryException;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.referencing.CRS;
import org.gpslw.annotation.FeatureInfo;
import org.locationtech.jts.geom.Geometry;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

public class SimpleFeatureUtils {

    private static final ConversionService conversionService = new DefaultConversionService();


    /**
     * 将 SimpleFeature 转换为指定类型的 Bean 对象
     *
     * @param feature  SimpleFeature 对象
     * @param beanClass 目标 Bean 类型
     * @return 实例化的 Bean 对象
     */
    public static <T> T toBean(SimpleFeature feature, Class<T> beanClass) {
        try {
            T bean = beanClass.getDeclaredConstructor().newInstance();
            BeanWrapper wrapper = new BeanWrapperImpl(bean);
            SimpleFeatureType type = feature.getFeatureType();
            for (PropertyDescriptor pd : getBeanProperties(beanClass)) {
                String propertyName = pd.getName();
                if ("class".equals(propertyName)) continue;
                if (type.getDescriptor(propertyName) != null) {
                    Object value = feature.getAttribute(propertyName);
                    Method writeMethod = pd.getWriteMethod();
                    if (writeMethod != null && value != null) {
                        // 获取目标字段类型
                        Class<?> targetType = pd.getPropertyType();
                        // 使用 Spring 转换服务进行类型转换，防止类型不一致导致的错误
                        Object convertedValue = conversionService.convert(value, targetType);
                        wrapper.setPropertyValue(propertyName, convertedValue);
                    }
                }
            }
            return bean;
        } catch (Exception e) {
            throw new RuntimeException("Failed to convert SimpleFeature to Bean", e);
        }
    }

    /**
     * 获取 Bean 的所有属性描述符
     */
    private static PropertyDescriptor[] getBeanProperties(Class<?> beanClass) throws IntrospectionException {
        BeanInfo beanInfo = java.beans.Introspector.getBeanInfo(beanClass);
        return beanInfo.getPropertyDescriptors();
    }

    /**
     * 批量转换多个 SimpleFeature 为 Bean 列表
     */
    public static <T> List<T> toBeans(List<SimpleFeature> features, Class<T> beanClass) {
        return features.stream()
                .map(feature -> toBean(feature, beanClass))
                .toList();
    }

    /**
     * 将 SimpleFeature 转换为 Map<String, Object>
     */
    public static Map<String, Object> toMap(SimpleFeature feature) {
        Map<String, Object> map = new HashMap<>();
        SimpleFeatureType type = feature.getFeatureType();
        type.getDescriptors().forEach(desc -> {
            String name = desc.getName().getLocalPart();
            map.put(name, feature.getAttribute(name));
        });
        return map;
    }

    /**
     * 将Java对象转换为SimpleFeature
     * 此方法用于将给定的Java对象（bean）转换为指定类型的SimpleFeature
     * 它通过反射机制读取Java对象的属性，并将这些属性设置到新创建的SimpleFeature中
     *
     * @param bean 要转换的Java对象，其属性将被映射到SimpleFeature的属性中
     * @return 转换后的SimpleFeature对象，具有与bean属性相对应的属性值
     * @throws RuntimeException 如果转换过程中发生错误，例如属性不可读或写入失败
     */
    public static SimpleFeature toSimpleFeature(Object bean, SimpleFeatureType featureType) {
        try {
            // 使用Spring的BeanWrapper包装bean对象，以便通过反射机制访问其属性
            BeanWrapper wrapper = new BeanWrapperImpl(bean);
            // 创建一个SimpleFeatureBuilder，用于构建SimpleFeature对象
            SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureType);

            FeatureId featureId = null;
            // 遍历featureType中的每个属性描述符
            featureType.getDescriptors().forEach(descriptor -> {
                // 获取当前属性的名称
                String propertyName = descriptor.getName().getLocalPart();
                // 检查bean对象是否具有可读的对应属性
                if (wrapper.isReadableProperty(propertyName)) {
                    // 从bean对象中获取属性值
                    Object value = wrapper.getPropertyValue(propertyName);
                    // 将属性值设置到builder中，用于构建SimpleFeature
                    builder.set(propertyName, value);
                }
            });
            // 使用builder构建并返回SimpleFeature对象，此处不指定featureId，可传入具体值
            return builder.buildFeature(null); // 可传入 featureId
        } catch (Exception e) {
            // 如果转换过程中发生异常，抛出RuntimeException
            throw new RuntimeException("Failed to convert Bean to SimpleFeature", e);
        }
    }

    //根据Class对象生成FeatureTYpe对象
    public static SimpleFeatureType getFeatureType(Class<?> clazz) {
        Field[] fields = clazz.getFields();
        SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
        FeatureInfo annotation = clazz.getAnnotation(FeatureInfo.class);
        String crs = "EPSG:4326";
        boolean longitudeFirst = false;
        String name = clazz.getSimpleName();
        if (annotation != null && !"".equals(annotation.crs())){
            crs = annotation.crs();
            longitudeFirst = annotation.longitudeFirst();
            name = annotation.name();
        }
        try {
            builder.setCRS(CRS.decode(crs, longitudeFirst));
        } catch (FactoryException e) {
            throw new RuntimeException(e);
        }
        builder.setName(name);
        for (Field field : fields) {
            if (field.getType().isAssignableFrom(Geometry.class)){
                builder.add(field.getName(), field.getType(), crs);
                builder.setDefaultGeometry(field.getName());
            }{
                builder.add(field.getName(), field.getType());
            }
        }
        return builder.buildFeatureType();
    }


}

